<template>
  <Teleport to="body">
    <div class="v-dialog">
      <el-dialog
        :model-value="modelValue"
        @update:model-value="closeModal"
        :show-close="false"
        draggable
        :width="width"
        @closed="onClosed"
        @open="onOpen"
        class="flex flex-col"
        :style="styleObj"
        align-center
        :destroy-on-close="destroyOnClose"
      >
        <template #header="{ close, titleId, titleClass }">
          <div
            class="flex bg-[#3078FF] h-45px px-18px justify-between items-center"
          >
            <span class="text-white text-15px">{{ title }}</span>
            <el-icon size="15px" color="white" @click="closeModal(close)"
              ><CloseBold
            /></el-icon>
          </div>
        </template>
        <div class="h-full" v-loading="loading">
          <el-scrollbar class="!h-full">
            <div class="py-30px px-20px" ref="bodyRef">
              <slot> 弹窗内容 </slot>
            </div>
          </el-scrollbar>
        </div>
        <template #footer>
          <slot name="footer">
            <div
              class="border-solid border-[#CACFDB] flex border-t-1px h-70px px-36px items-center justify-end"
            >
              <el-button
                v-if="showConfirm"
                type="primary"
                @click="emit('confirm')"
                :loading="actionLoading"
                >{{ confirmText }}</el-button
              >
              <el-button @click="closeModal">关闭</el-button>
            </div>
          </slot>
        </template>
      </el-dialog>
    </div>
  </Teleport>
</template>

<script setup lang="ts">
import { nextTick, onMounted, reactive, ref, watch } from "vue";

const props = withDefaults(
  defineProps<{
    modelValue: boolean;
    title: string; // 弹窗标题
    width?: string | number; // 弹窗宽度
    actionLoading?: boolean; // 确认按钮loading
    destroyOnClose?: boolean; // 是否在关闭后销毁
    showConfirm?: boolean; // 是否显示确认按钮
    loading?: boolean; // 弹窗内容动态加载loading，由外部控制
    confirmText?: string; // 确定按钮文字
  }>(),
  {
    showConfirm: true,
    loading: false,
    confirmText: '确定'
  }
);

const emit = defineEmits<{
  (e: "update:modelValue", value: boolean): void;
  (e: "confirm"): void;
  (e: "closed"): void;
}>();

const closeModal = (close: any) => {
  if (typeof close === "function") {
    close();
  }
  emit("update:modelValue", false);
};

const bodyRef = ref<HTMLDivElement>(); // v-dialog内容区域用此div包裹用于计算高度
const headerH = 45; // v-dialog头部区域高45px
const footerH = 70; // v-dialog底部区域高70px

// 使弹窗高度最高为屏幕高度
const styleObj = reactive<any>({});
const onOpen = () => {
  nextTick(() => {
    const bodyH = bodyRef.value?.clientHeight || 0;
    const elDialogH = headerH + bodyH + footerH; // 计算弹窗总高度
    if (elDialogH >= window.innerHeight - 1) {
      styleObj.height = window.innerHeight - 1 + "px";
    } else {
      styleObj.height = "auto";
    }
  });
};

// 异步内容加载状态改变时设置弹窗高度
watch(
  () => props.loading,
  () => {
    onOpen();
  }
);

// 弹窗关闭
const onClosed = () => {
  emit("closed");
};

// 暴露设置高度的方法
defineExpose({
  setHeight(height?: string) {
    if (height) {
      styleObj.height = height;
    } else {
      onOpen();
    }
  },
});
</script>

<style scoped>
.v-dialog :deep(.el-dialog__header) {
  padding: 0;
  margin: 0;
}
.v-dialog :deep(.el-dialog__footer) {
  padding: 0;
}

.v-dialog :deep(.el-dialog__body) {
  flex: 1;
  overflow: hidden;
  padding: 0;
}
</style>
